package org.hawk.gwt.ppc.parser.impl;

import java.io.InputStream;

import junit.framework.TestCase;

import org.hawk.gwt.ppc.parser.BlockDescriptor;
import org.hawk.gwt.ppc.parser.CastExpressionDescriptor;
import org.hawk.gwt.ppc.parser.CommaExpressionDescriptor;
import org.hawk.gwt.ppc.parser.Descriptor;
import org.hawk.gwt.ppc.parser.ExpressionDescriptor;
import org.hawk.gwt.ppc.parser.JavaParser;
import org.hawk.gwt.ppc.parser.LiteralExpressionDescriptor;
import org.hawk.gwt.ppc.parser.MethodCallDescriptor;
import org.hawk.gwt.ppc.parser.NameExpressionDescriptor;
import org.hawk.gwt.ppc.parser.NewOperatorExpressionDescriptor;
import org.hawk.gwt.ppc.parser.OperatorExpressionDescriptor;
import org.hawk.gwt.ppc.parser.TernaryExpressionDescriptor;
import org.hawk.gwt.ppc.parser.VariableDescriptor;
import org.hawk.gwt.ppc.parser.impl.mock.SimpleExpressionMock;
import org.hawk.gwt.ppc.utils.Name;
import org.hawk.gwt.ppc.utils.PreprocessorException;

public class SimpleExpressionsTestCase extends TestCase {
	
	public void testUnaryOperators() throws PreprocessorException {
		BlockDescriptor block = getBlock(SimpleExpressionMock.class, 0);
		
		assertEquals(7, block.size());
		
		unaryTest(((OperatorExpressionDescriptor)block.get(0)).getRightOperand(), "-", "a");
		unaryTest(((OperatorExpressionDescriptor)block.get(1)).getRightOperand(), "+", "a");
		unaryTest((ExpressionDescriptor)block.get(2), "++", "a");
		unaryTest((ExpressionDescriptor)block.get(3), "--", "a");
		unaryTest(((OperatorExpressionDescriptor)block.get(4)).getRightOperand(), "~", "a");
		unaryTest(((OperatorExpressionDescriptor)block.get(5)).getRightOperand(), "!", "b");
	}
	
	private void unaryTest(ExpressionDescriptor unary, String operator, String var) {
		assertTrue(unary instanceof OperatorExpressionDescriptor);
		
		OperatorExpressionDescriptor op = (OperatorExpressionDescriptor) unary;
		assertNull(op.getLeftOperand());
		assertNotNull(op.getRightOperand());
		assertEquals(operator, op.getOperator());
		assertTrue(op.getRightOperand() instanceof NameExpressionDescriptor);
		assertEquals(var, ((NameExpressionDescriptor)op.getRightOperand()).getName());
	}
	
	public void testPostfixOperators() throws PreprocessorException {
		BlockDescriptor block = getBlock(SimpleExpressionMock.class, 1);
		
		assertEquals(3, block.size());
		
		postfixTest((ExpressionDescriptor)block.get(0), "++");
		postfixTest((ExpressionDescriptor)block.get(1), "--");
	}
	
	private void postfixTest(ExpressionDescriptor postfix, String operator) {
		assertTrue(postfix instanceof OperatorExpressionDescriptor);
		
		OperatorExpressionDescriptor op = (OperatorExpressionDescriptor) postfix;
		assertNotNull(op.getLeftOperand());
		assertNull(op.getRightOperand());
		assertEquals(operator, op.getOperator());
		assertTrue(op.getLeftOperand() instanceof NameExpressionDescriptor);
		assertEquals("aaa", ((NameExpressionDescriptor)op.getLeftOperand()).getName());
	}
	
	public void testBinaryArifmeticOperators() throws PreprocessorException {
		BlockDescriptor block = getBlock(SimpleExpressionMock.class, 2);
		
		assertEquals(12, block.size());
		
		binaryTest(((OperatorExpressionDescriptor)block.get(0)).getRightOperand(), "+");
		binaryTest(((OperatorExpressionDescriptor)block.get(1)).getRightOperand(), "-");
		binaryTest(((OperatorExpressionDescriptor)block.get(2)).getRightOperand(), "*");
		binaryTest(((OperatorExpressionDescriptor)block.get(3)).getRightOperand(), "/");
		binaryTest(((OperatorExpressionDescriptor)block.get(4)).getRightOperand(), "%");
		binaryTest(((OperatorExpressionDescriptor)block.get(5)).getRightOperand(), ">>");
		binaryTest(((OperatorExpressionDescriptor)block.get(6)).getRightOperand(), "<<");
		binaryTest(((OperatorExpressionDescriptor)block.get(7)).getRightOperand(), ">>>");
		binaryTest(((OperatorExpressionDescriptor)block.get(8)).getRightOperand(), "|");
		binaryTest(((OperatorExpressionDescriptor)block.get(9)).getRightOperand(), "&");
		binaryTest(((OperatorExpressionDescriptor)block.get(10)).getRightOperand(), "^");
	}

	private void binaryTest(ExpressionDescriptor binary, String operator) {
		assertTrue(binary instanceof OperatorExpressionDescriptor);
		
		OperatorExpressionDescriptor op = (OperatorExpressionDescriptor) binary;
		assertNotNull(op.getLeftOperand());
		assertNotNull(op.getRightOperand());
		assertEquals(operator, op.getOperator());
		
		assertTrue(op.getLeftOperand() instanceof NameExpressionDescriptor);
		assertEquals("aaa", ((NameExpressionDescriptor)op.getLeftOperand()).getName());
		
		assertTrue(op.getRightOperand() instanceof NameExpressionDescriptor);
		assertEquals("bbb", ((NameExpressionDescriptor)op.getRightOperand()).getName());
	}
	
	public void testBooleanOperators() throws PreprocessorException {
		BlockDescriptor block = getBlock(SimpleExpressionMock.class, 3);
		
		assertEquals(3, block.size());
		
		binaryTest(((OperatorExpressionDescriptor)block.get(0)).getRightOperand(), "&&");
		binaryTest(((OperatorExpressionDescriptor)block.get(1)).getRightOperand(), "||");
	}
	
	public void testComparasion() throws PreprocessorException {
		BlockDescriptor block = getBlock(SimpleExpressionMock.class, 4);
		
		assertEquals(8, block.size());
		
		binaryTest(((OperatorExpressionDescriptor)block.get(0)).getRightOperand(), ">");
		binaryTest(((OperatorExpressionDescriptor)block.get(1)).getRightOperand(), "<");
		binaryTest(((OperatorExpressionDescriptor)block.get(2)).getRightOperand(), ">=");
		binaryTest(((OperatorExpressionDescriptor)block.get(3)).getRightOperand(), "<=");
		binaryTest(((OperatorExpressionDescriptor)block.get(4)).getRightOperand(), "==");
		binaryTest(((OperatorExpressionDescriptor)block.get(5)).getRightOperand(), "!=");
		
		OperatorExpressionDescriptor binary = (OperatorExpressionDescriptor)((OperatorExpressionDescriptor)block.get(6)).getRightOperand();
		
		assertTrue(binary instanceof OperatorExpressionDescriptor);
		
		OperatorExpressionDescriptor op = (OperatorExpressionDescriptor) binary;
		assertNotNull(op.getLeftOperand());
		assertNotNull(op.getRightOperand());
		assertEquals("instanceof", op.getOperator());
		
		assertTrue(op.getLeftOperand() instanceof NameExpressionDescriptor);
		assertEquals("sss", ((NameExpressionDescriptor)op.getLeftOperand()).getName());
		
		assertTrue(op.getRightOperand() instanceof NameExpressionDescriptor);
		assertEquals("Integer", ((NameExpressionDescriptor)op.getRightOperand()).getName());
	}
	
	public void testAssignmentOperator() throws PreprocessorException {
		BlockDescriptor block = getBlock(SimpleExpressionMock.class, 5);
		
		assertEquals(12, block.size());
		
		binaryTest((ExpressionDescriptor) block.get(0), "+=");
		binaryTest((ExpressionDescriptor) block.get(1), "/=");
		binaryTest((ExpressionDescriptor) block.get(2), "*=");
		binaryTest((ExpressionDescriptor) block.get(3), "-=");
		binaryTest((ExpressionDescriptor) block.get(4), "%=");
		binaryTest((ExpressionDescriptor) block.get(5), "^=");
		binaryTest((ExpressionDescriptor) block.get(6), "&=");
		binaryTest((ExpressionDescriptor) block.get(7), "|=");
		binaryTest((ExpressionDescriptor) block.get(8), ">>=");
		binaryTest((ExpressionDescriptor) block.get(9), ">>>=");
		binaryTest((ExpressionDescriptor) block.get(10), "<<=");
	}
	
	public void testDotOperator() throws PreprocessorException {
		BlockDescriptor block = getBlock(SimpleExpressionMock.class, 6);
		
		Descriptor binary = block.get(0);
		
		assertTrue(binary instanceof OperatorExpressionDescriptor);
		
		OperatorExpressionDescriptor op = (OperatorExpressionDescriptor) binary;
		assertNotNull(op.getLeftOperand());
		assertNotNull(op.getRightOperand());
		assertEquals("=", op.getOperator());
		
		assertTrue(op.getLeftOperand() instanceof OperatorExpressionDescriptor);
		assertTrue(op.getRightOperand() instanceof LiteralExpressionDescriptor);
		assertEquals(5, ((LiteralExpressionDescriptor)op.getRightOperand()).asInt());
		
		op = (OperatorExpressionDescriptor) op.getLeftOperand();
		assertNotNull(op.getLeftOperand());
		assertNotNull(op.getRightOperand());
		assertEquals(".", op.getOperator());
		
		assertTrue(op.getLeftOperand() instanceof NameExpressionDescriptor);
		assertEquals("this", ((NameExpressionDescriptor)op.getLeftOperand()).getName());
		
		assertTrue(op.getRightOperand() instanceof NameExpressionDescriptor);
		assertEquals("data", ((NameExpressionDescriptor)op.getRightOperand()).getName());
	}
	
	public void testLiterals() throws PreprocessorException {
		BlockDescriptor block = getBlock(SimpleExpressionMock.class, 7);
		assertLiteral((LiteralExpressionDescriptor) ((OperatorExpressionDescriptor)block.get(0)).getRightOperand(), 5);
		assertLiteral((LiteralExpressionDescriptor) ((OperatorExpressionDescriptor)block.get(1)).getRightOperand(), 10.0d);
		assertLiteral((LiteralExpressionDescriptor) ((OperatorExpressionDescriptor)block.get(2)).getRightOperand(), 5e-1f);
		assertLiteral((LiteralExpressionDescriptor) ((OperatorExpressionDescriptor)block.get(3)).getRightOperand(), 5l);
		assertLiteral((LiteralExpressionDescriptor) ((OperatorExpressionDescriptor)block.get(4)).getRightOperand(), '\u3214');
		assertLiteral((LiteralExpressionDescriptor) ((OperatorExpressionDescriptor)block.get(5)).getRightOperand(), "some string");
	}
	
	private void assertLiteral(LiteralExpressionDescriptor literal, Object obj) {
		assertNotNull(obj);
		assertNotNull(literal);
		assertEquals(literal.getType(), obj.getClass());
		
		if (obj instanceof String) {
			assertEquals(literal.asString(), obj);
		} else if (obj instanceof Character) {
			assertEquals(literal.asChar(), obj);
		} else if (obj instanceof Integer) {
			assertEquals(literal.asInt(), obj);
		} else if (obj instanceof Long) {
			assertEquals(literal.asLong(), obj);
		} else if (obj instanceof Double) {
			assertEquals(literal.asDouble(), obj);
		} else if (obj instanceof Float) {
			assertEquals(literal.asFloat(), obj);
		} else {
			fail();
		}
		
	}

	public void testTernaryOperator() throws PreprocessorException {
		BlockDescriptor block = getBlock(SimpleExpressionMock.class, 8);
		ExpressionDescriptor op = ((OperatorExpressionDescriptor)block.get(0)).getRightOperand();
		
		assertNotNull(op);
		assertTrue(op instanceof TernaryExpressionDescriptor);
		TernaryExpressionDescriptor ternary = (TernaryExpressionDescriptor) op;
		
		assertTrue(ternary.getCondition() instanceof NameExpressionDescriptor);
		assertTrue(ternary.getExpression() instanceof NameExpressionDescriptor);
		assertTrue(ternary.getFalseExpression() instanceof NameExpressionDescriptor);
		
		assertEquals("aaa", ((NameExpressionDescriptor)ternary.getCondition()).getName());
		assertEquals("bbb", ((NameExpressionDescriptor)ternary.getExpression()).getName());
		assertEquals("data", ((NameExpressionDescriptor)ternary.getFalseExpression()).getName());
	}
	
	public void testVariableDeclaration() throws PreprocessorException {
		BlockDescriptor block = getBlock(SimpleExpressionMock.class, 9);
		
		Descriptor desc = block.get(0);
		assertTrue(desc instanceof VariableDescriptor);
		VariableDescriptor var = (VariableDescriptor) desc;
		assertTrue(var.getAnnotations().isEmpty());
		assertTrue(var.getModifiers().isEmpty());
		assertEquals("var", var.getName());
		assertEquals("int", var.getType());
		assertEquals(block, var.getParent());
		
		ExpressionDescriptor op = ((OperatorExpressionDescriptor)block.get(2)).getLeftOperand();
		assertTrue(op instanceof VariableDescriptor);
		var = (VariableDescriptor) op;
		assertEquals(1, var.getAnnotations().size());
		assertEquals(1, var.getModifiers().size());
		assertEquals("newVar", var.getName());
		assertEquals("java.lang.Integer", var.getType());
	}
	
	public void testNewOperator() throws PreprocessorException {
		BlockDescriptor block = getBlock(SimpleExpressionMock.class, 10);
		
		Descriptor desc = block.get(0);
		assertTrue(desc instanceof NewOperatorExpressionDescriptor);
		NewOperatorExpressionDescriptor new1 = (NewOperatorExpressionDescriptor)desc;
		assertEquals("Integer", new1.getType());
		assertNull(new1.getAnonymousClass());
		assertEquals(block, new1.getParent());
		assertEquals(1, new1.getArguments().size());
		assertTrue(new1.getArguments().get(0) instanceof LiteralExpressionDescriptor);
		assertEquals("12", ((LiteralExpressionDescriptor)new1.getArguments().get(0)).asString());
		assertFalse(new1.isArray());
		assertNull(new1.getInitializerExpression());
		
		desc = ((OperatorExpressionDescriptor)block.get(1)).getRightOperand();
		assertTrue(desc instanceof NewOperatorExpressionDescriptor);
		NewOperatorExpressionDescriptor new2 = (NewOperatorExpressionDescriptor)desc;
		assertEquals("MemberClassMock", new2.getType());
		assertNull(new2.getAnonymousClass());
		assertEquals(0, new2.getArguments().size());
		assertFalse(new2.isArray());
		assertNull(new2.getInitializerExpression());
		
		desc = ((OperatorExpressionDescriptor)block.get(2)).getRightOperand();
		assertTrue(desc instanceof NewOperatorExpressionDescriptor);
		NewOperatorExpressionDescriptor new3 = (NewOperatorExpressionDescriptor)desc;
		assertEquals("InstanceMemberClass", new3.getType());
		assertNull(new3.getAnonymousClass());
		assertEquals(0, new3.getArguments().size());
		assertFalse(new3.isArray());
		assertNull(new3.getInitializerExpression());
		
		desc = block.get(3);
		assertTrue(desc instanceof NewOperatorExpressionDescriptor);
		NewOperatorExpressionDescriptor new4 = (NewOperatorExpressionDescriptor)desc;
		assertEquals("MemberClassMock.StaticMemberClass", new4.getType());
		assertNull(new4.getAnonymousClass());
		assertEquals(0, new4.getArguments().size());
		assertFalse(new4.isArray());
		assertNull(new4.getInitializerExpression());
		
		assertTrue(((OperatorExpressionDescriptor)block.get(4)).getRightOperand() instanceof NewOperatorExpressionDescriptor);
		assertTrue(((OperatorExpressionDescriptor)block.get(4)).getLeftOperand() instanceof NewOperatorExpressionDescriptor);
		
		desc = block.get(5);
		assertTrue(desc instanceof NewOperatorExpressionDescriptor);
		NewOperatorExpressionDescriptor new5 = (NewOperatorExpressionDescriptor)desc;
		assertEquals("java.lang.Runnable", new5.getType());
		assertNotNull(new5.getAnonymousClass());
		assertEquals("", new5.getAnonymousClass().getName());
		assertEquals(0, new5.getArguments().size());
		assertFalse(new5.isArray());
		assertNull(new5.getInitializerExpression());
		
		desc = ((OperatorExpressionDescriptor)block.get(6)).getRightOperand();
		assertTrue(desc instanceof NewOperatorExpressionDescriptor);
		NewOperatorExpressionDescriptor new6 = (NewOperatorExpressionDescriptor)desc;
		assertEquals("int[][][]", new6.getType());
		assertNull(new6.getAnonymousClass());
		assertEquals(3, new6.getArguments().size());
		assertTrue(new6.isArray());
		assertNull(new6.getInitializerExpression());
		
		desc = ((OperatorExpressionDescriptor)block.get(7)).getRightOperand();
		assertTrue(desc instanceof NewOperatorExpressionDescriptor);
		NewOperatorExpressionDescriptor new7 = (NewOperatorExpressionDescriptor)desc;
		assertEquals("int[]", new7.getType());
		assertNull(new7.getAnonymousClass());
		assertEquals(1, new7.getArguments().size());
		assertNotNull(new7.getInitializerExpression());
		assertEquals(4, new7.getInitializerExpression().getSize());
		assertTrue(new7.getInitializerExpression().get(0) instanceof LiteralExpressionDescriptor);
		assertTrue(new7.isArray());
	}
	
	public void testCast() throws PreprocessorException {
		BlockDescriptor block = getBlock(SimpleExpressionMock.class, 11);
		
		Descriptor desc = ((OperatorExpressionDescriptor)block.get(1)).getRightOperand();
		assertTrue(desc instanceof CastExpressionDescriptor);
		CastExpressionDescriptor cast1 = (CastExpressionDescriptor)desc;
		assertEquals("MemberClassMock", cast1.getType());
		assertTrue(cast1.getTarget() instanceof NameExpressionDescriptor);
		
		desc = ((OperatorExpressionDescriptor)block.get(2)).getRightOperand();
		assertTrue(desc instanceof CastExpressionDescriptor);
		CastExpressionDescriptor cast2 = (CastExpressionDescriptor)desc;
		assertEquals("MemberClassMock.StaticMemberClass", cast2.getType());
		assertTrue(cast2.getTarget() instanceof NameExpressionDescriptor);
		
		desc = ((OperatorExpressionDescriptor)block.get(3)).getRightOperand();
		assertTrue(desc instanceof CastExpressionDescriptor);
		CastExpressionDescriptor cast3 = (CastExpressionDescriptor)desc;
		assertEquals("MemberClassMock.InstanceMemberClass", cast3.getType());
		assertTrue(cast3.getTarget() instanceof NameExpressionDescriptor);
	}
	
	public void testMethodCall() throws PreprocessorException {
		BlockDescriptor block = getBlock(SimpleExpressionMock.class, 12);
		
		Descriptor desc = block.get(0);
		assertTrue(desc instanceof MethodCallDescriptor);
		MethodCallDescriptor m1 = (MethodCallDescriptor)desc;
		assertEquals(1, m1.getArguments().size());
		assertEquals("charAt", m1.getName());
		assertTrue(m1.getTarget() instanceof LiteralExpressionDescriptor);
		
		desc = block.get(1);
		assertTrue(desc instanceof MethodCallDescriptor);
		MethodCallDescriptor m2 = (MethodCallDescriptor)desc;
		assertEquals(2, m2.getArguments().size());
		assertTrue(m2.getArguments().get(0) instanceof LiteralExpressionDescriptor);
		assertTrue(m2.getArguments().get(1) instanceof LiteralExpressionDescriptor);
		assertEquals("substring", m2.getName());
		assertTrue(m2.getTarget() instanceof LiteralExpressionDescriptor);
	}
	
	public void testBrackets() throws PreprocessorException {
		BlockDescriptor block = getBlock(SimpleExpressionMock.class, 13);
		
		Descriptor desc = ((OperatorExpressionDescriptor)block.get(0)).getRightOperand();
		OperatorExpressionDescriptor op1 = (OperatorExpressionDescriptor)desc;
		assertTrue(op1.getLeftOperand() instanceof LiteralExpressionDescriptor);
		assertTrue(op1.getRightOperand() instanceof OperatorExpressionDescriptor);
		assertEquals("+", op1.getOperator());
		
		desc = ((OperatorExpressionDescriptor)block.get(1)).getRightOperand();
		OperatorExpressionDescriptor op2 = (OperatorExpressionDescriptor)desc;
		assertTrue(op2.getLeftOperand() instanceof OperatorExpressionDescriptor);
		assertTrue(op2.getRightOperand() instanceof LiteralExpressionDescriptor);
		assertEquals("*", op2.getOperator());
		
		desc = ((OperatorExpressionDescriptor)block.get(2)).getRightOperand();
		OperatorExpressionDescriptor op3 = (OperatorExpressionDescriptor)desc;
		assertTrue(op3.getLeftOperand() instanceof OperatorExpressionDescriptor);
		assertTrue(op3.getRightOperand() instanceof LiteralExpressionDescriptor);
		assertEquals("+", op3.getOperator());
	}
	
	public void testComaOperator() throws PreprocessorException {
		BlockDescriptor block = getBlock(SimpleExpressionMock.class, 14);
		assertTrue(block.get(0) instanceof CommaExpressionDescriptor);
		CommaExpressionDescriptor comma = (CommaExpressionDescriptor)block.get(0);
		assertEquals(3, comma.getExpressions().size());
		assertTrue(comma.getExpressions().get(0) instanceof OperatorExpressionDescriptor);
		assertTrue(comma.getExpressions().get(1) instanceof OperatorExpressionDescriptor);
		assertTrue(comma.getExpressions().get(2) instanceof OperatorExpressionDescriptor);
	}
	
	public void testSubexpressionsWithUnary() throws PreprocessorException {
		BlockDescriptor block = getBlock(SimpleExpressionMock.class, 15);

		assertTrue(block.get(0) instanceof MethodCallDescriptor);
		MethodCallDescriptor first = (MethodCallDescriptor) block.get(0);
		assertTrue(first.getTarget() instanceof NameExpressionDescriptor);
		assertEquals("sinkEvents", first.getName());
		
		assertTrue(first.getArguments().get(0) instanceof NameExpressionDescriptor);
		assertTrue(first.getArguments().get(1) instanceof OperatorExpressionDescriptor);
		
		OperatorExpressionDescriptor operator = (OperatorExpressionDescriptor) first.getArguments().get(1);
		assertEquals("&", operator.getOperator());
		assertTrue(operator.getLeftOperand() instanceof MethodCallDescriptor);
		assertEquals("getEventsSunk", ((MethodCallDescriptor)operator.getLeftOperand()).getName());
		assertTrue(operator.getRightOperand() instanceof OperatorExpressionDescriptor);
		
		operator = (OperatorExpressionDescriptor)operator.getRightOperand();
		
		assertNull(operator.getLeftOperand());
		assertEquals("~", operator.getOperator());
		assertTrue(operator.getRightOperand() instanceof NameExpressionDescriptor);
	}

	private BlockDescriptor getBlock(Class<?> clazz, int index) throws PreprocessorException {
		InputStream cu = getClass().getClassLoader().getResourceAsStream(Name.javaToFs(clazz.getCanonicalName()));
		try {
			JavaParser parser = JavaParser.create(cu);
			return parser.parse().getTopClasses().get(0).getMethods().get(index).getBody();
		} finally {
			try {
				cu.close();
			} catch(Exception ex) {
			}
		}
	}
}
